package cn.virens.config;

import java.util.Arrays;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.virens.oauth2.http.URLConnectionClient;
import cn.virens.oauth2.weixin.WeixinOauth2Client;
import cn.virens.web.components.shiro.SimpleShiroFilterFactoryBean;
import cn.virens.web.components.shiro.cookie.RememberMeCookie;
import cn.virens.web.components.shiro.oauth2.Oauth2AuthorizingRealm;
import cn.virens.web.components.shiro.oauth2.filter.Oauth2AuthorizingFilter;
import cn.virens.web.components.shiro.oauth2.filter.Oauth2LogoutFilter;
import cn.virens.web.components.shiro.oauth2.filter.Oauth2UserFilter;
import cn.virens.web.components.shiro.oauth2.weixin.WeixinPrincipalGetter;
import cn.virens.web.components.shiro.simple.SimpleAuthorizingRealm;
import cn.virens.web.components.shiro.simple.ajax.AjaxAuthorizingFilter;
import cn.virens.web.components.shiro.simple.ajax.AjaxLogoutFilter;
import cn.virens.web.components.shiro.simple.ajax.AjaxUserFilter;
import cn.virens.web.components.shiro.simple.simple.SimpleAuthorizingFilter;
import cn.virens.web.components.shiro.simple.simple.SimpleLogoutFilter;
import cn.virens.web.components.shiro.simple.simple.SimpleUserFilter;
import cn.virens.web.components.shiro.spring.SpringCacheManage;

/**
 * 权限框架配置
 * 
 * @作者   : loioi
 * @创建时间 :2017年8月1日 下午1:37:08
 */
@Configuration
public class SpringShiroOauth2Config {
	@Value("${auth.url.success}")
	private String successUrl;

	@Value("${auth.url.redirect}")
	private String redirectUrl;

	@Value("${auth.url.unauthorized}")
	private String unauthorizedUrl;

	@Value("${auth.api.url.user}")
	private String apiUserUrl;

	@Value("${auth.api.url.login}")
	private String apiLoginUrl;

	@Value("${auth.api.url.logout}")
	private String apiLogoutUrl;

	@Value("${auth.simple.url.user}")
	private String simpleUserUrl;

	@Value("${auth.simple.url.login}")
	private String simpleLoginUrl;

	@Value("${auth.simple.url.logout}")
	private String simpleLogoutUrl;

	@Value("${auth.captcha}")
	private Boolean captcha;

	@Value("${auth.param.captcha}")
	private String captchaName;

	@Value("${auth.param.username}")
	private String usernameName;

	@Value("${auth.param.password}")
	private String passwordName;

	@Value("${auth.param.rememberme}")
	private String rememberMeName;

	@Value("${auth.param.failurekey}")
	private String failureKeyAttribute;

	@Value("${auth.oauth.server.refresh_token}")
	private String oauthRefreshTokenUrl;

	@Value("${auth.oauth.server.access_token}")
	private String oauthAccessTokenUrl;

	@Value("${auth.oauth.server.authorize}")
	private String oauthAuthorizeUrl;

	@Value("${auth.oauth.client.secret}")
	private String oauthClientSecret;

	@Value("${auth.oauth.client.id}")
	private String oauthClientId;

	@Value("${auth.oauth.ssl}")
	private Boolean oauthSsl;
	@Value("${auth.oauth.charset}")
	private String oauthCharset;

	@Value("${auth.oauth.url.user}")
	private String oauthUserUrl;

	@Value("${auth.oauth.url.login}")
	private String oauthLoginUrl;

	@Value("${auth.oauth.url.logout}")
	private String oauthLogoutUrl;

	@Value("${auth.oauth.url.failure}")
	private String oauthFailureUrl;

	@Value("${auth.oauth.param.code}")
	private String oauthCodeParam;

	@Value("${auth.oauth.param.state}")
	private String oauthStateParam;

	@Value("${auth.oauth.param.error}")
	private String oauthErrorParam;

	@Value("${auth.oauth.param.error_desc}")
	private String oauthErrorDescParam;

	@Bean("oauth2-weixin")
	public WeixinOauth2Client weixinRequestClient() {
		WeixinOauth2Client client = new WeixinOauth2Client();
		client.setHttpClient(new URLConnectionClient());
		client.setRefreshTokenUrl(oauthRefreshTokenUrl);
		client.setAccessTokenUrl(oauthAccessTokenUrl);
		client.setAuthorizeUrl(oauthAuthorizeUrl);
		client.setClientSecret(oauthClientSecret);
		client.setClientId(oauthClientId);
		client.setCharset(oauthCharset);

		return client;
	}

	/**
	 * Shiro 缓存管理器配置
	 * 
	 * @param  cacheManager
	 * @return
	 */
	@Bean("shiro-cachemanager")
	public SpringCacheManage springCacheManage(org.springframework.cache.CacheManager cacheManager) {
		return new SpringCacheManage(cacheManager);
	}

	/**
	 * 会话管理器
	 * 
	 * @param  sessionDAO
	 * @return
	 */
	@Bean("shiro-sessionmanager")
	public ServletContainerSessionManager sessionManager() {
		return new ServletContainerSessionManager();
	}

	/**
	 * Realm实现
	 * 
	 * @param  authorizingProvider
	 * @return
	 */
	@Bean("shiro-simple-realm")
	public SimpleAuthorizingRealm authorizingRealm() {
		SimpleAuthorizingRealm realm = new SimpleAuthorizingRealm();
		realm.setAuthenticationCacheName("shiro-authenticationCache");
		realm.setAuthorizationCacheName("shiro-authorizationCache");
		realm.setAuthenticationCachingEnabled(true);
		realm.setAuthorizationCachingEnabled(true);

		return realm;
	}

	@Bean("shiro-oauth-realm")
	public Oauth2AuthorizingRealm oauth2AuthorizingRealm() {
		Oauth2AuthorizingRealm realm = new Oauth2AuthorizingRealm();
		realm.setAuthenticationCacheName("shiro-authenticationCache");
		realm.setAuthorizationCacheName("shiro-authorizationCache");
		realm.setOauth2Client(weixinRequestClient());
		realm.setPrincipalGetter(principalGetter());
		realm.setAuthenticationCachingEnabled(true);
		realm.setAuthorizationCachingEnabled(true);

		return realm;
	}

	private WeixinPrincipalGetter principalGetter() {
		WeixinPrincipalGetter principalGetter = new WeixinPrincipalGetter();
		principalGetter.setOauth2Client(weixinRequestClient());

		return principalGetter;
	}

	/**
	 * rememberMe管理器
	 * 
	 * @return
	 */
	@Bean("shiro-remembermemanager")
	public CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
		rememberMeManager.setCookie(new RememberMeCookie());
		rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));

		return rememberMeManager;
	}

	/**
	 * 安全管理器
	 * 
	 * @return
	 */
	@Bean("shiro-securitymanager")
	public DefaultWebSecurityManager securityManager(//
			@Qualifier("shiro-oauth-realm") Realm realm0, //
			@Qualifier("shiro-simple-realm") Realm realm1, //
			@Qualifier("shiro-cachemanager") SpringCacheManage cacheManager, //
			@Qualifier("shiro-sessionmanager") SessionManager sessionManager, //
			@Qualifier("shiro-remembermemanager") RememberMeManager rememberMeManager) {

		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

		securityManager.setRealms(Arrays.asList(realm0, realm1));
		securityManager.setRememberMeManager(rememberMeManager);
		securityManager.setSessionManager(sessionManager);
		securityManager.setCacheManager(cacheManager);

		return securityManager;
	}

	/**
	 * Shiro的Web过滤器
	 * 
	 * @return
	 * @throws Exception
	 */
	@Bean("shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(@Qualifier("shiro-securitymanager") SecurityManager securityManager) throws Exception {
		SimpleShiroFilterFactoryBean factoryBean = new SimpleShiroFilterFactoryBean();
		factoryBean.setSecurityManager(securityManager);
		factoryBean.setUnauthorizedUrl(unauthorizedUrl);
		factoryBean.setLoginUrl(simpleLoginUrl);
		factoryBean.setSuccessUrl(successUrl);

		factoryBean.addFilter("simpleAuthorizingFilter", simpleAuthorizingFilter());
		factoryBean.addFilter("simpleLogoutFilter", simpleLogoutFilter());
		factoryBean.addFilter("simpleUserFilter", simpleUserFilter());

		factoryBean.addFilter("oauth2AuthorizingFilter", oauth2AuthorizingFilter());
		factoryBean.addFilter("oauth2LogoutFilter", oauth2LogoutFilter());
		factoryBean.addFilter("oauth2UserFilter", oauth2UserFilter());

		factoryBean.addFilter("ajaxAuthorizingFilter", ajaxAuthorizingFilter());
		factoryBean.addFilter("ajaxLogoutFilter", ajaxLogoutFilter());
		factoryBean.addFilter("ajaxUserFilter", ajaxUserFilter());

		factoryBean.addFilterChain(simpleLoginUrl, "simpleAuthorizingFilter");
		factoryBean.addFilterChain(simpleLogoutUrl, "simpleLogoutFilter");
		factoryBean.addFilterChain(simpleUserUrl, "simpleUserFilter");

		factoryBean.addFilterChain(oauthFailureUrl, "anon");
		factoryBean.addFilterChain(oauthLoginUrl, "oauth2AuthorizingFilter");
		factoryBean.addFilterChain(oauthLogoutUrl, "oauth2LogoutFilter");
		factoryBean.addFilterChain(oauthUserUrl, "oauth2UserFilter");

		factoryBean.addFilterChain(apiLoginUrl, "ajaxAuthorizingFilter");
		factoryBean.addFilterChain(apiLogoutUrl, "ajaxLogoutFilter");
		factoryBean.addFilterChain(apiUserUrl, "ajaxUserFilter");

		factoryBean.addFilterChain("/**", "anon");
		return factoryBean;
	}

	/**
	 * 授权 注解配置
	 * 
	 * @return
	 */
	@Bean
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		return new DefaultAdvisorAutoProxyCreator();
	}

	/**
	 * 授权 注解配置
	 * 
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor attributeSourceAdvisor(@Qualifier("shiro-securitymanager") SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		attributeSourceAdvisor.setSecurityManager(securityManager);

		return attributeSourceAdvisor;
	}

	@Bean
	public AjaxUserFilter ajaxUserFilter() {
		AjaxUserFilter ajaxUserFilter = new AjaxUserFilter();
		ajaxUserFilter.setLoginUrl(apiLoginUrl);

		return ajaxUserFilter;
	}

	@Bean
	public AjaxLogoutFilter ajaxLogoutFilter() {
		return new AjaxLogoutFilter();
	}

	@Bean
	public AjaxAuthorizingFilter ajaxAuthorizingFilter() {
		AjaxAuthorizingFilter ajaxFormFilter = new AjaxAuthorizingFilter();
		ajaxFormFilter.setUseCaptcha(captcha);
		ajaxFormFilter.setLoginUrl(apiLoginUrl);
		ajaxFormFilter.setCaptchaParam(captchaName);
		ajaxFormFilter.setUsernameParam(usernameName);
		ajaxFormFilter.setPasswordParam(passwordName);

		return ajaxFormFilter;
	}

	@Bean
	public Oauth2UserFilter oauth2UserFilter() {
		Oauth2UserFilter oauth2UserFilter = new Oauth2UserFilter();
		oauth2UserFilter.setOauth2Request(weixinRequestClient());
		oauth2UserFilter.setLoginUrl(oauthLoginUrl);
		oauth2UserFilter.setUseSsl(oauthSsl);

		return oauth2UserFilter;
	}

	@Bean
	public Oauth2LogoutFilter oauth2LogoutFilter() {
		Oauth2LogoutFilter oauth2LogoutFilter = new Oauth2LogoutFilter();
		oauth2LogoutFilter.setRedirectUrl(redirectUrl);

		return oauth2LogoutFilter;
	}

	@Bean
	public Oauth2AuthorizingFilter oauth2AuthorizingFilter() {
		Oauth2AuthorizingFilter oauth2AuthorizingFilter = new Oauth2AuthorizingFilter();
		oauth2AuthorizingFilter.setOauth2Client(weixinRequestClient());
		oauth2AuthorizingFilter.setErrorDescParam(oauthErrorDescParam);
		oauth2AuthorizingFilter.setErrorParam(oauthErrorParam);
		oauth2AuthorizingFilter.setStateParam(oauthStateParam);
		oauth2AuthorizingFilter.setCodeParam(oauthCodeParam);

		oauth2AuthorizingFilter.setSuccessUrl(oauthAccessTokenUrl);
		oauth2AuthorizingFilter.setFailureUrl(oauthFailureUrl);
		oauth2AuthorizingFilter.setLoginUrl(oauthLoginUrl);
		oauth2AuthorizingFilter.setUseSsl(oauthSsl);

		return oauth2AuthorizingFilter;
	}

	@Bean
	public SimpleUserFilter simpleUserFilter() {
		SimpleUserFilter simpleUserFilter = new SimpleUserFilter();
		simpleUserFilter.setLoginUrl(simpleLoginUrl);

		return simpleUserFilter;
	}

	@Bean
	public SimpleLogoutFilter simpleLogoutFilter() {
		SimpleLogoutFilter simpleLogoutFilter = new SimpleLogoutFilter();
		simpleLogoutFilter.setRedirectUrl(redirectUrl);

		return simpleLogoutFilter;
	}

	@Bean
	public SimpleAuthorizingFilter simpleAuthorizingFilter() {
		SimpleAuthorizingFilter simpleAuthcFilter = new SimpleAuthorizingFilter();
		simpleAuthcFilter.setFailureKeyAttribute(failureKeyAttribute);
		simpleAuthcFilter.setRememberMeParam(rememberMeName);
		simpleAuthcFilter.setPasswordParam(passwordName);
		simpleAuthcFilter.setUsernameParam(usernameName);
		simpleAuthcFilter.setCaptchaParam(captchaName);
		simpleAuthcFilter.setLoginUrl(simpleLoginUrl);
		simpleAuthcFilter.setSuccessUrl(successUrl);
		simpleAuthcFilter.setUseCaptcha(captcha);

		return simpleAuthcFilter;
	}

}